package com.buymobile.dao.impl;


import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import com.buymobile.components.ibatis.CountStatementHelper;
import com.buymobile.components.ibatis.LimitSqlExecutor;
import com.buymobile.components.page.Page;
import com.buymobile.dao.SqlMapDao;
import com.buymobile.utils.ReflectUtil;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapException;
import com.ibatis.sqlmap.client.event.RowHandler;
import com.ibatis.sqlmap.engine.execution.SqlExecutor;
import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;
import com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate;

public class SqlMapDaoImpl extends SqlMapClientDaoSupport implements SqlMapDao {
	
	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger.getLogger(SqlMapDaoImpl.class);

	public static boolean IS_SETTED_SQLEXECUTOR = false;
	
	private SqlExecutor sqlExecutor;

	public Page queryForPage(final String statementName, final Object parameterObject,
			final int pageIndex, final int pageSize) {
		Page page = new Page(pageIndex,pageSize);
		page.setUserObjects(getSqlMapClientTemplate().queryForList(statementName, 
				parameterObject, (pageIndex-1)*pageSize, pageSize));
		prepareCountQuery(statementName);
		page.setRowCount(Integer.parseInt(getSqlMapClientTemplate().queryForObject(
				CountStatementHelper.getCountStatementId(statementName),parameterObject).toString()));
		return page;
	}

	protected void prepareCountQuery(String statementName) {

		String countQuery = CountStatementHelper.getCountStatementId(statementName);
		if (logger.isDebugEnabled()) {
			logger.debug("Convert " + statementName + " to " + countQuery);
		}
		SqlMapClient sqlMapClient = getSqlMapClientTemplate().getSqlMapClient();
		if (sqlMapClient instanceof ExtendedSqlMapClient) {
			SqlMapExecutorDelegate delegate = ((ExtendedSqlMapClient) sqlMapClient)
					.getDelegate();
			try {
				delegate.getMappedStatement(countQuery);
			} catch (SqlMapException e) {
				delegate.addMappedStatement(CountStatementHelper
						.createCountStatement(delegate
								.getMappedStatement(statementName)));
			}
		}
	}

	public Object queryForObject(String statementName)
			throws DataAccessException {
		return queryForObject(statementName, null);
	}

	public Object queryForObject(final String statementName,
			final Object parameterObject) throws DataAccessException {
		return getSqlMapClientTemplate().queryForObject(statementName,
				parameterObject);
	}

	public Object queryForObject(final String statementName,
			final Object parameterObject, final Object resultObject)
			throws DataAccessException {
		return getSqlMapClientTemplate().queryForObject(statementName,
				parameterObject, resultObject);
	}

	public List<?> queryForList(String statementName)
			throws DataAccessException {
		return queryForList(statementName, null);
	}

	public List<?> queryForList(final String statementName,
			final Object parameterObject) throws DataAccessException {
		return getSqlMapClientTemplate().queryForList(statementName,
				parameterObject);
	}

	public List<?> queryForList(String statementName, int skipResults,
			int maxResults) throws DataAccessException {
		return getSqlMapClientTemplate().queryForList(statementName, null,
				skipResults, maxResults);
	}

	public List<?> queryForList(final String statementName,
			final Object parameterObject, final int skipResults,
			final int maxResults) throws DataAccessException {
		return getSqlMapClientTemplate().queryForList(statementName,
				parameterObject, skipResults, maxResults);
	}

	public void queryWithRowHandler(String statementName, RowHandler rowHandler)
			throws DataAccessException {

		getSqlMapClientTemplate().queryWithRowHandler(statementName, null,
				rowHandler);
	}

	public void queryWithRowHandler(final String statementName,
			final Object parameterObject, final RowHandler rowHandler)
			throws DataAccessException {
		getSqlMapClientTemplate()
				.queryWithRowHandler(statementName, rowHandler);
	}

	public Map<?, ?> queryForMap(final String statementName,final Object parameterObject, 
			final String keyProperty)	throws DataAccessException {
		return getSqlMapClientTemplate().queryForMap(statementName,
				parameterObject, keyProperty);
	}

	public Map<?, ?> queryForMap(final String statementName,final Object parameterObject, 
			final String keyProperty,final String valueProperty) throws DataAccessException {
		return getSqlMapClientTemplate().queryForMap(statementName, 
				parameterObject, keyProperty,valueProperty);
	}

	public Object insert(String statementName) throws DataAccessException {
		return getSqlMapClientTemplate().insert(statementName, null);
	}

	public Object insert(final String statementName,
			final Object parameterObject) throws DataAccessException {
		return getSqlMapClientTemplate().insert(statementName, parameterObject);
	}

	public int update(String statementName) throws DataAccessException {
		return getSqlMapClientTemplate().update(statementName, null);
	}

	public int update(final String statementName, final Object parameterObject)
			throws DataAccessException {

		return getSqlMapClientTemplate().update(statementName, parameterObject);
	}

	public void update(String statementName, Object parameterObject,
			int requiredRowsAffected) throws DataAccessException {
		getSqlMapClientTemplate().update(statementName, parameterObject,
				requiredRowsAffected);
	}

	public int delete(String statementName) throws DataAccessException {
		return getSqlMapClientTemplate().delete(statementName, null);
	}

	public int delete(final String statementName, final Object parameterObject)
			throws DataAccessException {
		return getSqlMapClientTemplate().delete(statementName, parameterObject);
	}

	public void delete(String statementName, Object parameterObject,
			int requiredRowsAffected) throws DataAccessException {
		getSqlMapClientTemplate().delete(statementName, parameterObject,
				requiredRowsAffected);
	}

	public void setEnableLimit(boolean enableLimit) {
		if (sqlExecutor instanceof LimitSqlExecutor) {
			((LimitSqlExecutor) sqlExecutor).setEnableLimit(enableLimit);
		}
	}

	public void initialize() throws Exception {
		if (sqlExecutor != null && IS_SETTED_SQLEXECUTOR == false) {
			SqlMapClient sqlMapClient = getSqlMapClientTemplate()
					.getSqlMapClient();
			if (sqlMapClient instanceof ExtendedSqlMapClient) {
				ReflectUtil.setFieldValue(((ExtendedSqlMapClient) sqlMapClient)
						.getDelegate(), "sqlExecutor", SqlExecutor.class,
						sqlExecutor);
				IS_SETTED_SQLEXECUTOR = true;
			}
		}
	}

	public SqlExecutor getSqlExecutor() {
		return sqlExecutor;
	}

	public void setSqlExecutor(SqlExecutor sqlExecutor) {
		this.sqlExecutor = sqlExecutor;
	}

}
